package com.flkin.security;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.flkin.common.utils.RedisUtils;
import com.flkin.common.utils.StringUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author yanxiang.huang
 */
@Setter
@Slf4j
public class TokenFilter extends GenericFilterBean {

    private TokenProvider tokenProvider;
    private RedisUtils redisUtils;
    private SecurityProperties properties;

    public TokenFilter(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = tokenProvider.getToken(httpServletRequest);
        log.info("access {} {}", httpServletRequest.getMethod(), httpServletRequest.getRequestURI());
        if (StringUtils.isNotEmpty(token) && redisUtils.hasKey(RedisUtils.TOKEN_KEY + token)) {
            Authentication authentication = tokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            log.info("current user:{}", authentication.getName());
            refresh(token, redisUtils);
        }
        chain.doFilter(request, response);
    }

    public void refresh(String token, RedisUtils redisUtils) {
        long time = redisUtils.getExpire(RedisUtils.TOKEN_KEY + token);
        Date expireDate = DateUtil.offset(new Date(), DateField.SECOND, (int) time);
        log.debug("expire at {} seconds after", time);
        long differ = expireDate.getTime() - System.currentTimeMillis();
        if (differ <= properties.getTokenDetectSeconds() * 1000) {
            log.info("refresh token, increase {} seconds", properties.getTokenDetectSeconds());
            long renew = time + properties.getTokenRefreshSeconds();
            redisUtils.expire(RedisUtils.TOKEN_KEY + token, renew, TimeUnit.SECONDS);
        }
    }

}
